package org.teamh.artificialIntelligence;

import org.teamh.connection.json.command.JsonSender;
import org.teamh.game.repository.MapRepository;
import org.teamh.gui.Event;
import org.teamh.gui.InterfaceManager;
import org.teamh.gui.Screen;
import org.teamh.gui.ingame.IngameScreen;
import org.teamh.gui.loadingscreen.LoadingScreen;
import org.teamh.gui.lobby.LobbyScreen;
import org.teamh.gui.login.LoginScreen;
import org.teamh.gui.mapeditor.MapEditorScreen;
import org.teamh.logic.datamodel.DatamodelManager;
import org.teamh.logic.datamodel.Tile;
import org.teamh.logic.datamodel.User;
import org.teamh.logic.datamodel.UserAssets;

import de.lessvoid.nifty.Nifty;

/**
 * This class is the base for the AI, and is comparable with the graphical user
 * interface of the client.
 * 
 * @author richard
 * 
 */
public class AIInterfaceManager extends Thread implements InterfaceManager{

	public static DatamodelManager datamodelManager;
	public static User user;
	public static StartAI startAI;
	
	private String name;
	private String password;
	private String game;
	
	private Tile startTile;
	
	private boolean loggedIn;
	private boolean inGame;
	private boolean gameStarted;
	
	private static boolean isRunning;
	
	/**
	 * Creates a new AIInterfaceManager, which logs in, with the specified name
	 * and password into the specified game.
	 * 
	 * @param startAI
	 * @param name
	 * @param password
	 * @param game
	 */
	public AIInterfaceManager(StartAI startAI, String name, String password, String game) {
		AIInterfaceManager.startAI = startAI;
		this.name = name;
		this.password = password;
		this.game = game;
	}
	
	@Override
	public void run() {
		System.out.println("started ai with: " + name + " and password: " + password);
		datamodelManager = startAI.getLogic().getManager();
		
		//log into the game
		login();
		
		//waits, until the player is logged in
		waitForLogin();
		
		//join the game
		joinGame();
		
		//wait, until the game is loaded
		waitForGameLoaded();
		
		//get the user object
		grabUser();
		
		//select a start tile
		selectStartTile();
		
		//load info for building towers
		LoadWorld.load(datamodelManager);
		Initiator.getPriorityCells();
		
		//wait, until the game is started
		waitForStartGame();
		
		//play the game
		playGame();
		
		//leave the game
		leaveGame();
		
		waits(300);
		
		//remove dependencies
		System.out.println(name + ": closing");
		startAI.exit();
		System.out.println(name + ": closed");
	}
	
	private void waitForLogin() {
		System.out.println(name + ": waits, until it is logged in");
		while(!loggedIn) {
			waits(20);
		}
	}
	
	/**
	 * This methods waits, until the game is loaded. It only waits for 100
	 * seconds. If the game not exists, the AI will exit
	 */
	private void waitForGameLoaded() {
		int i = 0;
		for(; i < 1000 && !startAI.getModel().isManagerReady(); i++) {
			System.out.println(name + ": waits for load game " + i + "/999");
			waits(100);
		}
		if(i == 1000) {
			System.out.println("AI could not join the game");
			System.exit(0);
		}
		startAI.getModel().setManagerReady(false);
		inGame = true;
		System.out.println(name + ": game is loaded");
	}

	/**
	 * This method logs the player into the game
	 */
	private void login() {
		System.out.println(name + ": login");
		startAI.getConnection().sendMessage("login " + name + " " + password);
	}
	
	/**
	 * This methods joins the selected game
	 */
	private void joinGame() {
		System.out.println(name + ": joins the game: " + game);
		startAI.getConnection().sendMessage("join game " + game);
	}
	
	/**
	 * This method leaves the current game
	 */
	private void leaveGame() {
		System.out.println(name + ": leave the game");
		JsonSender.leaveGame();
	}
	
	/**
	 * This method grabs the user name from the user assets of the datamodel. 
	 */
	private void grabUser() {
		System.out.println(name + ": grabs the user");
		for(UserAssets u: startAI.getLogic().getManager().getUserAssets().values()) {
			if(u.getUser() != null && u.getUser().getNickname() != null && u.getUser().getNickname().equals(name)) {
				user = u.getUser();
			}
		}
		
		if(user == null) {
			System.out.println(name + ": could not grab user from game");
			System.out.println(name + ": closes the game");
			System.exit(0);
		}
	}
	
	/**
	 * This method selects a start tile and waits, until the start tile is selected correctly. 
	 */
	private void selectStartTile() {
		do {
			selectRandomStartTile();
		} while(!waitForSelectedStartTile());
	}
	
	/**
	 * With this method, a random start tile is selected. 
	 */
	private void selectRandomStartTile() {
		System.out.println(name + ": chooses a start tile by random");
		
		for(Tile t: startAI.getLogic().getManager().getTiles().values()) {
			if(t.getUserAssets() == null) {
				startTile = t;
				JsonSender.chooseTile(t.getID());
				return;
			}
		}
		
		System.out.println(name + ": there is no empty start tile");
		System.out.println(name + ": closes program");
		System.exit(0);
	}
	
	/**
	 * This method waits, until the start tile is selected. If
	 */
	private boolean waitForSelectedStartTile() {
		System.out.println(name + ": waits for the selection of the start tile");
		for(int i = 0; i < 100; i++) {
			if(startTile.getUserAssets() != null && startTile.getUserAssets().getUser() != null) {
				if(startTile.getUserAssets().getUser() == user) {
					System.out.println(name + ": starttile was selected correctly");
					return true;
				} else {
					System.out.println(name + ": startile was not selected correctly");
					return false;
				}
			}
			waits(20);
		}
		
		return false;
	}
	
	/**
	 * Waits, until the current game is started. 
	 */
	private void waitForStartGame() {
		System.out.println(name + ": waits, until the game started");
		while(!gameStarted) {
			waits(20);
		}
	}
	
	/**
	 * This method plays the game
	 */
	private void playGame() {
		isRunning = true;
		CreepUpdater creepUpdater = new CreepUpdater();
		creepUpdater.start();
		System.out.println(name + ": is ingame and is going to kill you");
		
		System.out.println(name + ": plays the game");
		
		while(isRunning && startAI.getLogic().getGame().isRunning() && isGamePlaying() && startAI.getLogic().getGame().getWinner() == null) {			
			waits(10);
			System.out.println("is running");
			
			if(Simulator.creepListChanged(user)) {
				if(Defender.checkForDefending()) {
					Defender.defend();
				} else {
					Attacker.attack();
				}
			} else {
				Attacker.attack();
			}
		}
		
		creepUpdater.setRunning(false);
	}
	
	/**
	 * Checks, if the game is still ongoing for the ai. 
	 * @return
	 */
	private boolean isGamePlaying() {
		return true;
//		for(UserAssets u: startAI.getLogic().getManager().getUserAssets().values()) {
//			if(u.getUser() == user) {
//				return true;
//			}
//		}
//		
//		return false;
	}
	
	/**
	 * This method waits for the requested time. 
	 * @param millis
	 */
	public static void waits(long millis) {
		try {
			Thread.sleep(millis);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}

	@Override
	public void close() {
		System.out.println(name + ": request to close");
		MapRepository.clear();
	}

	@Override
	public void addEvent(Event event) {
		System.out.println("Event: " + event.getKey() + " " + event.getValue());
		
		if(event.getKey().equals("closed")) {
			isRunning = false;
		} else if(event.getKey().equals("ErrorOccured")) {
			if(event.getValue().equals("Wrong paramter")) {
				if(loggedIn && !inGame) {
					System.out.println(name + ": has wrong game name. Program exit");
					System.exit(0);
				}
			}
		} else if(event.getKey().equals("login")) {
			if(event.getValue().equals("true")) {
				loggedIn = true;
			} else {
				System.out.println(name + ": wrong login parameters. Program exit. ");
				System.exit(0);
			}
		} else if(event.getKey().equals("running")) {
			gameStarted = true;
		} else if(event.getKey().equals("winner")) {
			isRunning = false;
		}
	}

	@Override
	public Nifty getNifty() {
		return null;
	}

	@Override
	public Screen getCurrentScreen() {
		return null;
	}

	@Override
	public LoadingScreen getLoadingScreen() {
		return null;
	}

	@Override
	public LoginScreen getLoginScreen() {
		return null;
	}

	@Override
	public LobbyScreen getLobbyScreen() {
		return null;
	}

	@Override
	public MapEditorScreen getMapEditorScreen() {
		return null;
	}

	@Override
	public IngameScreen getIngameScreen() {
		return null;
	}

	@Override
	public void setCurrentScreen(Screen currentScreen) {
		
	}

	@Override
	public void setFollowingScreen(Screen followingScreen) {
		
	}

	public static boolean isRunning() {
		return isRunning;
	}
}
